package com.ng.desktop.fpa.services;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ng.desktop.fpa.model.Bitacora;
import com.ng.desktop.fpa.model.DetalleFactura;
import com.ng.desktop.fpa.model.DetalleFacturaDBF;
import com.ng.desktop.fpa.model.FacturaDBF;
import com.ng.desktop.fpa.persistence.BitacoraPersistenceHB;
import com.ng.desktop.fpa.persistence.DetalleFacturaPersistenceDBF;
import com.ng.desktop.fpa.persistence.DetalleFacturaPersistenceHB;
import com.ng.desktop.fpa.persistence.FacturaPersistenceDBF;
import com.ng.desktop.fpa.report.AnuladosReport;
import com.ng.framework.util.NGUtil;

public class FPAnulados {

	private static final Logger LOG = LoggerFactory.getLogger(FPAnulados.class);

	private DetalleFacturaPersistenceDBF detalleFacturaPersistenceDBF;
	private FacturaPersistenceDBF facturaPersistenceDBF;
	private DetalleFacturaPersistenceHB detalleFacturaPersistenceHB;
	private BitacoraPersistenceHB bitacoraPersistenceHB;
	private NGConfiguration configurationBean;

	public FPAnulados() {

		detalleFacturaPersistenceDBF = (DetalleFacturaPersistenceDBF) NGFactoryBean.getInstance().getBean(
				"detalleFacturaPersistenceDBF");

		facturaPersistenceDBF = (FacturaPersistenceDBF) NGFactoryBean.getInstance().getBean("facturaPersistenceDBF");

		detalleFacturaPersistenceHB = (DetalleFacturaPersistenceHB) NGFactoryBean.getInstance().getBean(
				"detalleFacturaPersistenceHB");
		bitacoraPersistenceHB = (BitacoraPersistenceHB) NGFactoryBean.getInstance().getBean("bitacoraPersistenceHB");

		configurationBean = (NGConfiguration) NGFactoryBean.getInstance().getBean("ngConfigurationBean");
	}

	private Date getFechaUltimoReporte() {

		Date now = NGUtil.getNow();
		
		Integer diasReporte = Integer.valueOf(configurationBean.getDiasReporte());

		return NGUtil.addTime(now, Calendar.DAY_OF_MONTH, diasReporte);

	}

	public void reporte() {

		Date now = NGUtil.getNow();

		LOG.info("Proceso de Reporte comenzado fecha :" + NGUtil.ddMMyyyy_dateFormat.format(now));

		// Traer todos los detalles con estado anulado
		List<DetalleFactura> allAnulados = detalleFacturaPersistenceHB.getAllAnulados();
		LOG.info("allAnulados :" + allAnulados.size());

		Map<String, DetalleFactura> articulosAnulados = new HashMap<String, DetalleFactura>();

		for (DetalleFactura detalleFactura : allAnulados) {
			articulosAnulados.put(detalleFactura.getArticuloId(), detalleFactura);
		}
		try {
			AnuladosReport report = new AnuladosReport(now);
			report.setObjects(new ArrayList<DetalleFactura>(articulosAnulados.values()));
			report.buildReport();

			String pdfPath = configurationBean.getPdfExportPath() + System.getProperty("file.separator")
					+ NGUtil.yyyyMMdd_dateFormat.format(now) + ".pdf";

			File pdf = new File(pdfPath);
			OutputStream fos = new FileOutputStream(pdf);
			fos.write(report.getPDF());
			fos.flush();
			fos.close();

			Bitacora bitacora = new Bitacora();

			bitacora.setArticulosAnulados(articulosAnulados.values().size());
			bitacora.setDetallesAnulados(allAnulados.size());
			bitacora.setFecha(now);

			bitacoraPersistenceHB.create(bitacora);

			detalleFacturaPersistenceHB.deleteAll();

			LOG.info("Proceso de Reporte Finalizado - Bitácora:" + bitacora);

		} catch (Exception e) {
			LOG.error("Proceso de Reporte Finalizado con Errores", e);
		}

	}

	public void update() {

		long initialTime = System.currentTimeMillis();

		LOG.info("Proceso de Update comenzado");

		// Get current size of heap in bytes
		LOG.debug("Heap Size: " + (Runtime.getRuntime().totalMemory() / (1024 * 1024)));
		// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
		// Any attempt will result in an OutOfMemoryException.
		LOG.debug("Heap Max Size: " + (Runtime.getRuntime().maxMemory() / (1024 * 1024)));

		// Get amount of free memory within the heap in bytes. This size will increase
		// after garbage collection and decrease as new objects are created.
		LOG.debug("Heap Free Size: " + (Runtime.getRuntime().freeMemory() / (1024 * 1024)));

		Date fecha = this.getFechaUltimoReporte();
		LOG.info("getFechaUltimoReporte :" + NGUtil.ddMMyyyy_dateFormat.format(fecha));

		// Traigo todas las facturas dbf desde el ultimo reporte
		List<FacturaDBF> facturasByFechaUltimoReporte = facturaPersistenceDBF.getAllFacturaByFecha(fecha);
		LOG.info("Facturas desde la fecha de último reporte :" + facturasByFechaUltimoReporte.size());

		if (facturasByFechaUltimoReporte.size() == 0) {
			LOG.info("Proceso de Update Finalizado ya que no hay facturas a analizar");
			return;
		}

		// Traigo todos detalles de facturas DBF de las ventas del paso anterior
		Map<String, DetalleFacturaDBF> detalleFacturasByFacturasMap = detalleFacturaPersistenceDBF
				.getAllDetalleFacturaByFacturas(facturasByFechaUltimoReporte);
		LOG.info("Detalle de Facturas DBF (de las facturas del paso anterior) :" + detalleFacturasByFacturasMap.size());

		// Traigo todos los detalles HB de las ventas del paso anterior
		List<DetalleFactura> detallesFacturaHB = detalleFacturaPersistenceHB.getAll(facturasByFechaUltimoReporte);
		LOG.info("Detalles de Facturas HB :" + detallesFacturaHB.size());

		// Inicializo los anulados
		List<DetalleFactura> anulados = new ArrayList<DetalleFactura>();

		// Por cada detalle de MySQL me fijo si existe en DBF
		for (DetalleFactura detalleFacturaHB : detallesFacturaHB) {

			DetalleFacturaDBF detalleFacturaDBF = detalleFacturasByFacturasMap.get(detalleFacturaHB.getKey());

			if (detalleFacturaDBF != null) {
				// Si existe lo elimino
				detalleFacturasByFacturasMap.remove(detalleFacturaDBF.getKey());
				LOG.debug("detalleFacturasByFacturasMap.remove :" + detalleFacturaDBF);

			} else {
				// Sino existe, significa que se anulo y lo tengo que añadir a la lista de anulados
				anulados.add(detalleFacturaHB);
				LOG.info("anulados.add:" + detalleFacturaHB);
			}
		}

		// Esta es la lista de detalles de factura que se agregaron desde la ultima vez
		List<DetalleFacturaDBF> nuevos = new ArrayList(detalleFacturasByFacturasMap.values());
		LOG.info("Detalle de Facturas DBF Nuevos :" + nuevos.size());

		for (DetalleFacturaDBF detalleFacturaDBF : nuevos) {

			DetalleFactura detalleFacturaHB = new DetalleFactura();
			detalleFacturaHB.setArticuloId(detalleFacturaDBF.getCref());
			detalleFacturaHB.setArticuloDescripcion(detalleFacturaDBF.getCdetalle());
			detalleFacturaHB.setFactura(detalleFacturaDBF.getFactura());
			detalleFacturaPersistenceHB.create(detalleFacturaHB);
		}

		// Por cada anulado, actualizo el estado
		LOG.info("Anulados :" + anulados.size());
		for (DetalleFactura detalleFactura : anulados) {
			detalleFactura.setEstado(DetalleFactura.ESTADO_ANULADO);
			detalleFacturaPersistenceHB.update(detalleFactura);
		}

		LOG.info("Proceso de Update Finalizado - Tiempo: " + (System.currentTimeMillis() - initialTime) / 1000 + " sec");

	}
}
